#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <functional>
#include <fstream>
#include <string.h>

const static std::string base_sep = "\r\n";
const static std::string line_sep = ": ";
const static std::string spacesep = " ";
const static std::string suffix_sep = ".";
const static std::string prefixwebpath = "wwwroot";
const static std::string homepage = "index.html";
const static std::string httpversion = "HTTP/1.0";
const static std::string html_404 = "404.html";
const static std::string arg_sep = "?";

class httpRequest
{
public:
    httpRequest()
        : _blankline(base_sep), _path(prefixwebpath)
    {
    }

    std::string GETline(std::string &reqstr)
    {
        auto pos = reqstr.find(base_sep);
        if (pos == std::string::npos)
        {
            return std::string();
        }
        std::string line = reqstr.substr(0, pos);
        reqstr.erase(0, line.size() + base_sep.size());
        if (line.empty())
        {
            return base_sep;
        }
        return line;
    }

    void ParseRequestLine()
    {
        // 将字符串转换成字符流，
        std::stringstream ss(_reqline);
        // 对于GET方法来说，可以向 _url 传参，所以需要考虑选择 GET方法后
        // 应该怎么加入参数字段
        ss >> _method >> _url >> _version;

        // 比较两个字符串是否相等 (忽略大小写)
        if (strcasecmp(_method.c_str(), "GET") == 0)
        {
            auto pos = _url.find(arg_sep);
            if (pos != std::string::npos)
            {
                _requestbodytext = _url.substr(pos + arg_sep.size());
                _url.resize(pos);
            }
        }

        _path += _url;
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }

        // wwwroot/index.html
        // wwwroot/image/1.png
        auto pos = _path.rfind(suffix_sep);
        if (pos != std::string::npos)
        {
            _suffix = _path.substr(pos);
        }
        else
        {
            _suffix = ".default";
        }
    }

    void ParseRequestHeader()
    {
        for (auto &head : _requestheaders)
        {
            int pos = head.find(line_sep);
            if (pos == std::string::npos)
            {
                continue;
            }
            std::string key = head.substr(0, pos);
            std::string val = head.substr(pos + line_sep.size());
            if (key.empty() || val.empty())
            {
                continue;
            }
            _headersKV.insert(std::make_pair(key, val));
        }
    }

    void Deserialize(std::string &reqstr)
    {
        _reqline = GETline(reqstr);
        std::string header;
        do
        {
            header = GETline(reqstr);
            if (header.empty())
            {
                break;
            }
            else if (header == base_sep)
            {
                break;
            }
            _requestheaders.push_back(header);
        } while (true);

        if (!reqstr.empty())
        {
            _requestbodytext = reqstr;
        }

        // 再进一步
        ParseRequestLine();
        ParseRequestHeader();
    }

    std::string Url()
    {
        LOG(DEBUG, "client want url %s\n", _url.c_str());
        return _url;
    }

    std::string Path()
    {
        LOG(DEBUG, "client want path %s\n", _path.c_str());
        return _path;
    }

    std::string Getfilesuffix()
    {
        return _suffix;
    }

    std::string GetMethod()
    {
        LOG(DEBUG, "client request method is  %s\n", _method.c_str());
        return _method;
    }

    std::string GetRequestBody()
    {
        LOG(DEBUG, "client request method is %s, args: %s,request path: %s\n", _method.c_str(), _requestbodytext.c_str(), _path.c_str());
        return _requestbodytext;
    }

    void Print()
    {
        std::cout << "------------------------------" << std::endl;
        std::cout << "###" << _reqline << std::endl;
        for (auto &header : _requestheaders)
        {
            std::cout << "@@@" << header << std::endl;
        }
        std::cout << "***" << _blankline;
        std::cout << ">>>" << _requestbodytext << std::endl;

        std::cout << "Method: " << _method << std::endl;
        std::cout << "url: " << _url << std::endl;
        std::cout << "version" << _version << std::endl;

        for (auto &_headofKV : _headersKV)
        {
            std::cout << "~~~" << _headofKV.first << "->" << _headofKV.second << std::endl;
        }
    }

    ~httpRequest()
    {
    }

private:
    std::string _reqline;
    std::vector<std::string> _requestheaders;
    std::string _blankline;
    std::string _requestbodytext;

    std::string _method;
    std::string _url;
    std::string _path;
    std::string _suffix;
    std::string _version;
    std::unordered_map<std::string, std::string> _headersKV;
};

class httpResponse
{
public:
    httpResponse()
        : _version(httpversion), _blankline(base_sep)
    {
    }

    void addCode(int code, const std::string &desc)
    {
        _statuscode = code;
        _desc = desc;
    }

    void addHeader(const std::string &k, const std::string &v)
    {
        _headerKV[k] = v;
    }

    void addBodyText(const std::string &bodytest)
    {
        _responsebodytext = bodytest;
    }

    std::string serialize()
    {
        // 1. 构建状态行
        _statusline = _version + spacesep + std::to_string(_statuscode) + spacesep + _desc + base_sep;

        // 2. 构建应答报头
        for (auto &header : _headerKV)
        {
            std::string headerline = header.first + line_sep + header.second + base_sep;
            _responseheaders.push_back(headerline);
        }

        // 3. 空行和正文

        // 4. 正式序列化
        std::string responsestr = _statusline;
        for (auto &line : _responseheaders)
        {
            responsestr += line;
        }
        responsestr += _blankline;
        responsestr += _responsebodytext;

        return responsestr;
    }

    ~httpResponse()
    {
    }

private:
    // httpresponse 属性
    std::string _version;
    int _statuscode;
    std::string _desc;
    std::unordered_map<std::string, std::string> _headerKV;

    // 基本的httpresponse的格式
    std::string _statusline;
    std::vector<std::string> _responseheaders;
    std::string _blankline;
    std::string _responsebodytext;
};

using func_t = std::function<httpResponse(httpRequest&)>;


class httpServer
{
private:
    std::string getFileContent(const std::string &path)
    {
        // 以二进制读 html 文件
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
        {
            return std::string();
        }
        in.seekg(0, in.end);
        // size 将得到当前读时  的偏移量
        int filesize = in.tellg();
        in.seekg(0, in.beg);

        std::string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);

        in.close();
        return content;
    }

public:
    httpServer()
    {
        _mimeType.insert(std::make_pair(".html", "text/html"));
        _mimeType.insert(std::make_pair(".jpg", "image/jpeg"));
        _mimeType.insert(std::make_pair(".png", "image/png"));
        _mimeType.insert(std::make_pair(".default", "text/html"));

        _codetodesc.insert(std::make_pair(100, "continue"));
        _codetodesc.insert(std::make_pair(200, "ok"));
        _codetodesc.insert(std::make_pair(201, "created"));
        _codetodesc.insert(std::make_pair(301, "move permanently"));
        _codetodesc.insert(std::make_pair(302, "found"));
        _codetodesc.insert(std::make_pair(404, "not found"));
    }

    // #define TEST

    std::string handleHttpRequest(std::string &reqstr) // req 曾经被客户端序列化过
    {
#ifdef TEST
        std::cout << "-------------------------" << std::endl;
        std::cout << reqstr;

        std::string responsestr = "HTTP/1.1 200 OK\r\n";
        responsestr += "Content-Type: text/html\r\n";
        responsestr += "\r\n";
        responsestr += "<html><h1>hello Linux!!!!</h1></html>";

        return responsestr;
#else
        std::cout << "-------------------------" << std::endl;
        std::cout << reqstr;
        std::cout << "-------------------------" << std::endl;

        httpRequest req;
        httpResponse resp;

        req.Deserialize(reqstr);
        // req.GetMethod();

        if (req.Path() == "wwwroot/redir")
        {
            // 进行重定向
            std::string redirpath = "https://www.qq.com";
            resp.addCode(302, _codetodesc[302]);
            resp.addHeader("location", redirpath);
        }
        else if (!req.GetRequestBody().empty())
        {
            if (isService(req.Path()))
            {
                resp = _serviceList[req.Path()](req);
            }
        }
        else
        {
            // 最基本的上层处理
            std::string content = getFileContent(req.Path());
            if (content.empty())
            {

                content = getFileContent("wwwroot/404.html");
                resp.addCode(404, _codetodesc[404]);
                resp.addHeader("Content-Length", std::to_string(content.size()));
                resp.addHeader("Content-Type", _mimeType[req.Getfilesuffix()]);
                resp.addBodyText(content);
            }
            else
            {
                resp.addCode(200, _codetodesc[200]);
                resp.addHeader("Content-Length", std::to_string(content.size()));
                resp.addHeader("Content-Type", _mimeType[".html"]);
                resp.addHeader("Set-Cookie", "username=zhangsan");
                resp.addBodyText(content);
            }
        }

        return resp.serialize();
#endif
    }

    void insertService(const std::string serviceName, func_t f)
    {
        std::string s = prefixwebpath + serviceName;
        _serviceList[s] = f;
    }

    bool isService(const std::string &servicename)
    {
        auto iter = _serviceList.find(servicename);
        if (iter == _serviceList.end())
        {
            return false;
        }
        return true;
    }

    ~httpServer()
    {
    }

private:
    std::unordered_map<std::string, std::string> _mimeType;
    std::unordered_map<int, std::string> _codetodesc;
    std::unordered_map<std::string, func_t> _serviceList;
};
